﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using Entity = PSE.Deployment.TeamFoundation.Entity;
using WorkItemTrackingClient = Microsoft.TeamFoundation.WorkItemTracking.Client;
using PSE.Deployment.TeamFoundation.Entity.Enum;
using System.Threading;
using Microsoft.TeamFoundation;
using Microsoft.TeamFoundation.VersionControl.Client;
using System.IO;

namespace PSE.Deployment.TeamFoundation.Communication
{
    public class WorkItem : CommunicationBase
    {

        #region Singleton
        private static WorkItem instance;
        protected WorkItem()
        {
            Communication.VersionControl.Instance.TraceEvent += (sender, e) => RaiseEventTrace(e);
        }
        public static WorkItem Instance
        {
            get
            {
                if (instance == null)
                {
                    Mutex mutex = new Mutex();
                    mutex.WaitOne();

                    if (instance == null)
                        instance = new WorkItem();

                    mutex.Close();
                }
                return instance;
            }
        }
        #endregion Singleton

        public string GetEnumDescription(Enum value)
        {
            RaiseEventTrace(string.Format("Obtendo a descrição do Enum", value.ToString()));

            FieldInfo fi = value.GetType().GetField(value.ToString());
            DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);

            string description = (attributes.Length > 0) ? attributes[0].Description : value.ToString();
            RaiseEventTrace(string.Format("Descrição do Enum {0}: {1}", value.ToString(), description));
            return description;
        }

        private WorkItemTrackingClient.WorkItemStore GetWorkItemStore()
        {
            RaiseEventTrace("Obtendo o WorkItemStore");

            Microsoft.TeamFoundation.Client.TeamFoundationServer teamFoundationServer = TeamServer.Instance.GetSinacorTeamServer();
            WorkItemTrackingClient.WorkItemStore workItemScore = (WorkItemTrackingClient.WorkItemStore)teamFoundationServer.GetService(typeof(WorkItemTrackingClient.WorkItemStore));

            return workItemScore;
        }

        private PSE.Deployment.TeamFoundation.Entity.WorkItem WrapType(Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem workItemTeamSystem)
        {
            if (workItemTeamSystem == null)
                throw new ArgumentNullException("workItemTeamSystem");

            RaiseEventTrace(string.Format("Convertendo um Workitem do Tipo Microsoft para o Tipo PSE com o Id {0}", workItemTeamSystem.Id));

            PSE.Deployment.TeamFoundation.Entity.WorkItem workItem = new PSE.Deployment.TeamFoundation.Entity.WorkItem();
            workItem.Id = workItemTeamSystem.Id;
            workItem.SystemId = workItemTeamSystem.Fields["Bovespa.SystemId"].Value.ToString();

            if (string.IsNullOrEmpty(workItemTeamSystem.Fields["Bovespa.ProcessId"].Value.ToString()))
                workItem.ProcessId = 0;
            else
                workItem.ProcessId = int.Parse(workItemTeamSystem.Fields["Bovespa.ProcessId"].Value.ToString());

            workItem.TeamProject = workItemTeamSystem.Fields["System.TeamProject"].Value.ToString();
            workItem.Description = workItemTeamSystem.Description;
            workItem.Title = workItemTeamSystem.Title;

            //TODO: Colocar um campo no HotFix para armazenar o GroupId
            workItem.ReleaseGroupId = workItemTeamSystem.Title.Split(' ')[0].Replace(" ", "");

            workItem.CreatedBy = workItemTeamSystem.CreatedBy;
            workItem.StateDescription = workItemTeamSystem.State;
            workItem.Type = (PSE.Deployment.TeamFoundation.Entity.Enum.WorkItemType)Enum.Parse(typeof(PSE.Deployment.TeamFoundation.Entity.Enum.WorkItemType), workItemTeamSystem.Type.Name);
            workItem.PackageIteration = workItemTeamSystem.Fields["Bovespa.PackageIteration"].Value.ToString();

            string version = string.Empty;

            if (workItemTeamSystem.Fields.Contains("VersaoHF") && !string.IsNullOrEmpty(workItemTeamSystem.Fields["VersaoHF"].Value.ToString()))
                version = workItemTeamSystem.Fields["VersaoHF"].Value.ToString();
            else if (workItemTeamSystem.Fields.Contains("VersaoSP"))
                version = workItemTeamSystem.Fields["VersaoSP"].Value.ToString();

            RaiseEventTrace(string.Format("Versão do WorkItem {0}: {1}", workItemTeamSystem.Id, version));
            string[] versionParts = version.Split('.');

            if (versionParts.Count() == 4)
            {
                workItem.Version.Major = int.Parse(versionParts[0]);
                workItem.Version.Minor = int.Parse(versionParts[1]);

                int result;

                if (int.TryParse(versionParts[2].ToString(), out result))
                    workItem.Version.Revision = result;
                else
                    workItem.Version.Revision = 0;

                workItem.Version.Build = int.Parse(versionParts[3]);
            }

            workItem.CreatedDate = workItemTeamSystem.CreatedDate;

            return workItem;
        }


        public List<PSE.Deployment.TeamFoundation.Entity.WorkItem> GetAllOpenedHotFixes()
        {
            RaiseEventTrace("Listando todos os WorkItens abertos do Tipo HotFix");

            Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore workItemStore = GetWorkItemStore();

            Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemCollection workItemsCollection = workItemStore.Query("SELECT [System.WorkItemType], [System.Id], [System.CreatedBy], [System.Title], [System.TeamProject], [Bovespa.VersaoHF], [Bovespa.VersaoSP], [Sinacor.ErroGeracao], [System.State], [Bovespa.PackageIteration] FROM WorkItems WHERE [System.WorkItemType] = 'HotFix' and [System.State] in ('Aberto', 'Processando') ORDER BY [System.Id]");

            List<PSE.Deployment.TeamFoundation.Entity.WorkItem> workItems = new List<PSE.Deployment.TeamFoundation.Entity.WorkItem>();

            foreach (Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem workItemCollection in workItemsCollection)
            {
                PSE.Deployment.TeamFoundation.Entity.WorkItem workItem = WrapType(workItemCollection);

                RaiseEventTrace(string.Format("Adicionando WorkItem {0} aberto do Tipo HotFix", workItem.Id));
                workItems.Add(workItem);
            }
            return workItems;
        }

        public List<PSE.Deployment.TeamFoundation.Entity.WorkItem> GetAllRevision(PSE.Deployment.TeamFoundation.Entity.TeamProject teamProject, PSE.Deployment.TeamFoundation.Entity.PackageIterationBranch packageIterationBranch)
        {
            if (teamProject == null)
                throw new ArgumentNullException("teamProject");

            if (teamProject == null)
                throw new ArgumentNullException("teamProject");

            RaiseEventTrace(string.Format("Obtendo as revisões do TeamProject {0}", teamProject.Name));

            Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore workItemStore = GetWorkItemStore();

            Dictionary<string, string> queryParameters = new Dictionary<string, string>();
            queryParameters.Add("TeamProject", teamProject.Name);
            queryParameters.Add("WorkItemType1", "HotFix");
            queryParameters.Add("WorkItemType2", "ServicePack");
            queryParameters.Add("PackageIteration", packageIterationBranch.BranchVersion.ToString());

            Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemCollection workItemsCollection = workItemStore.Query("SELECT [System.WorkItemType], [System.Id], [System.CreatedBy], [System.Title], [Bovespa.VersaoHF], [System.TeamProject], [Bovespa.VersaoSP], [Sinacor.ErroGeracao], [System.State], [Bovespa.PackageIteration]  FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [Bovespa.PackageIteration] = @PackageIteration AND ([System.WorkItemType] = @WorkItemType1 OR [System.WorkItemType] = @WorkItemType2) ORDER BY [System.Id]", queryParameters);

            List<PSE.Deployment.TeamFoundation.Entity.WorkItem> workItems = new List<PSE.Deployment.TeamFoundation.Entity.WorkItem>();

            foreach (Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem workItemCollection in workItemsCollection)
            {
                PSE.Deployment.TeamFoundation.Entity.WorkItem workItem = WrapType(workItemCollection);

                RaiseEventTrace(string.Format("Adicionando workitem {0}", workItem.Id));
                workItems.Add(workItem);
            }

            return workItems;
        }

        public List<PSE.Deployment.TeamFoundation.Entity.WorkItem> GetAllRevision(PSE.Deployment.TeamFoundation.Entity.TeamProject teamProject, string versionLabel)
        {
            if (teamProject == null)
                throw new ArgumentNullException("teamProject");

            if (string.IsNullOrEmpty(versionLabel))
                throw new ArgumentNullException("versionLabel");

            RaiseEventTrace(string.Format("Obtendo as revisões do TeamProject {0} com versionLabel {1}", teamProject.Name, versionLabel));

            Entity.Version version = new Entity.Version();
            Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore workItemStore = GetWorkItemStore();

            Dictionary<string, string> queryParameters = new Dictionary<string, string>();

            queryParameters.Add("TeamProject", teamProject.Name);
            queryParameters.Add("WorkItemType1", "HotFix");
            queryParameters.Add("WorkItemType2", "ServicePack");
            queryParameters.Add("Versao", versionLabel.Substring(0, 4));

            Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemCollection workItemsCollection = workItemStore.Query("SELECT [System.WorkItemType], [System.Id], [System.CreatedBy], [System.Title], [Bovespa.VersaoHF], [System.TeamProject], [Bovespa.VersaoSP], [Sinacor.ErroGeracao], [System.State], [Bovespa.PackageIteration] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND ([System.WorkItemType] = @WorkItemType1 OR [System.WorkItemType] = @WorkItemType2) AND ([Bovespa.VersaoHF] Contains @Versao or [Bovespa.VersaoSP] Contains @Versao) AND ([System.WorkItemType] = @WorkItemType1 OR [System.WorkItemType] = @WorkItemType2) ORDER BY [System.Id]", queryParameters);

            List<PSE.Deployment.TeamFoundation.Entity.WorkItem> workItems = new List<PSE.Deployment.TeamFoundation.Entity.WorkItem>();

            foreach (Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem workItemCollection in workItemsCollection)
            {
                PSE.Deployment.TeamFoundation.Entity.WorkItem workItem = WrapType(workItemCollection);

                RaiseEventTrace(string.Format("Adicionando workitem {0}", workItem.Id));
                workItems.Add(workItem);
            }

            return workItems;
        }

        public void ChangeWorkItemField(int workItemId, Entity.WorkItemField field, string value)
        {
            if (workItemId <= 0)
                throw new ArgumentException("Invalid WorkItemId", "workItemId");

            if (string.IsNullOrEmpty(value))
                throw new ArgumentNullException("value");

            WorkItemTrackingClient.WorkItem workItem;

            Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore workItemStore = GetWorkItemStore();
            try
            {
                RaiseEventTrace(string.Format("Obtendo o Workitem {0}", workItemId));
                workItem = workItemStore.GetWorkItem(workItemId);
            }
            catch (DeniedOrNotExistException)
            {
                throw new Exception("Work Item inválido.");
            }

            string fieldDescription = GetEnumDescription(field);
            string message = "Alterando o campo " + fieldDescription + " para " + value + ". Work Item-> " + workItem.ToString();
            RaiseEventTrace(message);
            workItem.Fields[fieldDescription].Value = value;
            RaiseEventTrace(string.Format("Salvando o Workitem {0}", workItemId));
            workItem.Save();
        }

        public int OpenHotFix(PSE.Deployment.TeamFoundation.Entity.TeamProject teamProject, PSE.Deployment.TeamFoundation.Entity.PackageIterationBranch packageIterationBranch, string releaseGroupName, string systemId, string releaseVersion)
        {
            RaiseEventTrace("Criando um Workitem do Tipo HotFix");
            return OpenWorkItem(teamProject, packageIterationBranch, releaseGroupName, systemId, releaseVersion, PSE.Deployment.TeamFoundation.Entity.Enum.WorkItemType.HotFix);
        }

        public int OpenServicePack(PSE.Deployment.TeamFoundation.Entity.TeamProject teamProject, PSE.Deployment.TeamFoundation.Entity.PackageIterationBranch packageIterationBranch, string releaseGroupName, string systemId, string releaseVersion)
        {
            RaiseEventTrace("Criando um Workitem do ServicePack");
            return OpenWorkItem(teamProject, packageIterationBranch, releaseGroupName, systemId, releaseVersion, PSE.Deployment.TeamFoundation.Entity.Enum.WorkItemType.ServicePack);
        }

        public Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem GetWorkItem(PSE.Deployment.TeamFoundation.Entity.WorkItem workItem)
        {
            if (workItem == null)
                throw new ArgumentNullException("workItem");

            RaiseEventTrace(string.Format("Obtendo um workitem do Tipo Microsoft pelo id {0}", workItem.Id));

            Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore workItemScore = GetWorkItemStore();
            Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem workItemTFS = workItemScore.GetWorkItem(workItem.Id);

            return workItemTFS;
        }

        private int OpenWorkItem(PSE.Deployment.TeamFoundation.Entity.TeamProject teamProject,
                                        PSE.Deployment.TeamFoundation.Entity.PackageIterationBranch packageIterationBranch,
                                        string releaseGroupName,
                                        string systemId,
                                        string releaseVersion,
                                        PSE.Deployment.TeamFoundation.Entity.Enum.WorkItemType workItemType)
        {

            if (teamProject == null)
                throw new ArgumentNullException("teamProject");

            if (packageIterationBranch == null)
                throw new ArgumentNullException("packageIterationBranch");

            if (string.IsNullOrEmpty(releaseGroupName))
                throw new ArgumentNullException("releaseGroupName");

            if (string.IsNullOrEmpty(systemId))
                throw new ArgumentNullException("systemId");

            if (string.IsNullOrEmpty(releaseVersion))
                throw new ArgumentNullException("releaseVersion");

            string fieldName = string.Empty;



            switch (workItemType)
            {
                case PSE.Deployment.TeamFoundation.Entity.Enum.WorkItemType.HotFix:
                    fieldName = "Bovespa.VersaoHF";
                    break;
                case PSE.Deployment.TeamFoundation.Entity.Enum.WorkItemType.ServicePack:
                    fieldName = "Bovespa.VersaoSP";
                    break;
            }

            Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore workItemStore = GetWorkItemStore();
            Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemType wiType = workItemStore.Projects[teamProject.Name].WorkItemTypes[workItemType.ToText()];

            Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem newWI = new Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem(wiType);
            newWI.Title = string.Format("{0} {1}", releaseGroupName, workItemType.ToText());
            newWI.State = "Aberto";
            newWI.Fields["System.TeamProject"].Value = teamProject.Name;
            newWI.Fields["Bovespa.SystemId"].Value = systemId;
            newWI.Fields["Bovespa.PackageIteration"].Value = packageIterationBranch.BranchVersion.ToString();
            newWI.Fields[fieldName].Value = releaseVersion;
            newWI.Save();

            RaiseEventTrace(string.Format("Criando um Workitem do tipo {0} com Id {1}", workItemType.ToText(), newWI.Id));
            return newWI.Id;
        }

        public string GenerateVersion(PSE.Deployment.TeamFoundation.Entity.Enum.WorkItemType workItemType, string versionLabel, string teamProjectName, string packageIterationBranch)
        {
            if (string.IsNullOrEmpty(teamProjectName))
                throw new ArgumentNullException("packageIterationBranch");

            if (string.IsNullOrEmpty(packageIterationBranch))
                throw new ArgumentNullException("packageIterationBranch");

            RaiseEventTrace(string.Format("Gerando uma versão para um workitem do tipo {0} com o label {1} no teamproject {2} no branch {3}", workItemType.ToText(), versionLabel, teamProjectName, packageIterationBranch));

            Entity.Version version = new Entity.Version();
            Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore workItemStore = GetWorkItemStore();

            Dictionary<string, string> queryParameters = new Dictionary<string, string>();

            queryParameters.Add("TeamProject", teamProjectName);
            queryParameters.Add("WorkItemType1", "HotFix");
            queryParameters.Add("WorkItemType2", "ServicePack");
            queryParameters.Add("PackageIteration", packageIterationBranch);
            queryParameters.Add("Versao", versionLabel.Substring(0, 4));

            Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemCollection workItemsCollection = workItemStore.Query("SELECT [System.Id], [Bovespa.VersaoHF], [Bovespa.VersaoSP] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [Bovespa.PackageIteration] = @PackageIteration AND ([System.WorkItemType] = @WorkItemType1 OR [System.WorkItemType] = @WorkItemType2)  and [System.State] = 'Finalizado com sucesso' and ([Bovespa.VersaoHF] Contains @Versao or [Bovespa.VersaoSP] Contains @Versao) ORDER BY [System.Id] desc", queryParameters);

            string[] versionElements = versionLabel.Split('.');

            if (workItemsCollection.Count == 0)
            {
                version.Major = int.Parse(versionElements[0]);
                version.Minor = int.Parse(versionElements[1]); ;

                if (workItemType == PSE.Deployment.TeamFoundation.Entity.Enum.WorkItemType.HotFix)
                {
                    version.MinorRevision = 1;
                    version.MajorRevision = 0;
                }
                else
                {
                    version.MinorRevision = 0;
                    version.MajorRevision = 1;
                }

                version.Build = int.Parse(versionElements[3]); ;
            }
            else
            {
                Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem wi = ((Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem)workItemsCollection[0]);
                string versionTemp = string.Empty;

                if (wi.Fields.Contains("VersaoHF") && wi.Fields["VersaoHF"].Value != null && !string.IsNullOrEmpty(wi.Fields["VersaoHF"].Value.ToString()))
                    versionTemp = wi.Fields["VersaoHF"].Value.ToString();
                else if (wi.Fields.Contains("VersaoSP"))
                    versionTemp = wi.Fields["VersaoSP"].Value.ToString();

                string[] versionTempElements = versionTemp.Split('.');

                version.Major = int.Parse(versionElements[0]);
                version.Minor = int.Parse(versionElements[1]); ;
                version.Revision = int.Parse(versionTempElements[2]);
                version.Build = int.Parse(versionElements[3]);

                if (workItemType == PSE.Deployment.TeamFoundation.Entity.Enum.WorkItemType.HotFix)
                {
                    version.MinorRevision = Convert.ToInt16(version.MinorRevision + 1);
                }
                else if (workItemType == PSE.Deployment.TeamFoundation.Entity.Enum.WorkItemType.HotFix)
                {
                    version.MinorRevision = 0;
                    version.MajorRevision = Convert.ToInt16(version.MajorRevision + 1);
                }
            }

            RaiseEventTrace(string.Format("Versão Gerada {0}", version.ToString()));
            return version.ToString();
        }

        /// <summary>
        /// Verifica se existem workitems atrelados a um workitem
        /// </summary>
        /// <param name="workItem">Workitem</param>
        /// <param name="usedList">lista com id's de workitems</param>
        /// <returns>true/false caso exista ou não</returns>
        private bool HasWorkItem(WorkItemTrackingClient.WorkItem workItem, List<int> usedList)
        {
            bool hasElement = usedList.Contains(workItem.Id);

            if (!hasElement)
            {
                usedList.Add(workItem.Id);
            }

            return hasElement;
        }

        /// <summary>
        /// Obtém um WorkItem
        /// </summary>
        /// <param name="workItemStore">Informações sobre o workItem</param>
        /// <param name="workItem">WorkItem relacionado</param>
        /// <param name="usedList">lista com id's de workitems</param>
        /// <param name="downloadedClasses">Arquivos baixados</param>
        /// <param name="downloadPath">Caminho para download</param>
        public void GetWorkItem(WorkItemTrackingClient.WorkItemStore workItemStore,
                                 WorkItemTrackingClient.WorkItem workItem,
                                 List<int> usedList,
                                 List<string> downloadedClasses,
                                 string downloadPath)
        {

            if (workItemStore == null)
                throw new ArgumentNullException("workItemStore");

            if (workItem == null)
                throw new ArgumentNullException("workItem");

            if (usedList == null)
                throw new ArgumentNullException("usedList");

            if (downloadedClasses == null)
                throw new ArgumentNullException("downloadedClasses");

            if (string.IsNullOrEmpty(downloadPath))
                throw new ArgumentNullException("downloadPath");

            RaiseEventTrace("Obtendo um WorkItem");

            if (!HasWorkItem(workItem, usedList))
            {
                foreach (WorkItemTrackingClient.Link link in workItem.Links)
                {
                    if (link is WorkItemTrackingClient.RelatedLink)
                    {
                        WorkItemTrackingClient.RelatedLink relatedLink = (WorkItemTrackingClient.RelatedLink)link;
                        WorkItemTrackingClient.WorkItem workItemLinked = workItemStore.GetWorkItem(relatedLink.RelatedWorkItemId);
                        GetWorkItem(workItemStore, workItemLinked, usedList, downloadedClasses, downloadPath);
                    }
                    else if (link is WorkItemTrackingClient.ExternalLink)
                    {
                        WorkItemTrackingClient.ExternalLink extLink = (WorkItemTrackingClient.ExternalLink)link;
                        ArtifactId artifact = LinkingUtilities.DecodeUri(extLink.LinkedArtifactUri);
                        if (String.Equals(artifact.ArtifactType, "Changeset"))
                        {
                            Uri linkUri = new Uri(extLink.LinkedArtifactUri);
                            var versionControl = Communication.VersionControl.Instance.GetVersionControlServer();
                            var changeset = versionControl.ArtifactProvider.GetChangeset(linkUri);
                            foreach (Change change in changeset.Changes)
                            {
                                string filename = Communication.Changeset.Instance.FormatChangeSetItemDownloadPath(downloadPath, change.Item.ServerItem);
                                change.Item.DownloadFile(filename);
                                downloadedClasses.Add(filename);
                            }
                        }
                    }
                }
            }
        }
    }
}
